/*
	Copyright (C) 2010 2011 Rémi Thebault

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __pc_task_h__
#define __pc_task_h__

#include <QDebug>
#include <QObject>
#include <QList>
#include <QString>
#include <QEvent>

#include <procycle/link.h>
#include <procycle/xml_storable.h>



//! the PC namespace contains the whole ProCycle API


namespace PC {



	class TaskError {};


	class MacroTask;
	class Link;
	class Task;
	class Cycle;



	const QString TASK_ELEMENT_NAME = "task";


	/*!	\brief	the Task class is the base class to all tasks within a cycle.

		This class is abstract and provides basic task functionality.
		Among other things, a task has a name, a length, and links to
		other tasks. Within a cycle, tasks have a hierarchy, which means
		tasks have parent tasks and can have child tasks.
		Topmost tasks parent is the cycle to which they are tied.
	 */
	class Task : public QObject, public virtual XmlStorable
	{
		Q_OBJECT

	public:

		typedef QList<Task*>::iterator iterator;
		typedef QList<Task*>::const_iterator const_iterator;


		enum Data {
			DATA_NUM = -1,
			DATA_TBSID = 0,
			DATA_POS,
			DATA_NAME,
			DATA_LENGTH,
			DATA_START,
			DATA_FINISH,
			DATA_LATESTART,
			DATA_LATEFINISH,
			DATA_PREDECESSORS,

			// the following are accessible through QObject::properties
			DATA_COLOR,			// property "color"		QString
			DATA_EXPANDED,		// property "expanded"	bool

			DATA_COUNT
		};


		/*!	\brief	Regular contructor.

			Constructs a task provided a direct parent and the cycle which is the topmost parent*
			of this whole hierarchy
		 */
		Task(Cycle* cycle, MacroTask* parent=0);


		/*! \brief	Copy constructor
		 */
		Task(Task* other);

		virtual ~Task();





		Cycle* cycle() const {
			return m_cycle;
		}
		void setCycle(Cycle* cycle) {
			m_cycle = cycle;
		}


		MacroTask* parent() const {
			return m_parent;
		}
		void setParent(MacroTask* parent) {
			m_parent = parent;
		}


		PC::Task* directCycleChildAncestor ();



		int num() const {
			return m_num;
		}
		virtual int lastNum() const = 0;

		virtual int setNum(int current) = 0;

		int pos() const;


		/*!	\brief	the Task Breakdown Structure Id

			An unique identifier for the task within the task breakdown structure.
			The format is [toplevel ind].[...].[parent ind].[this task ind]
		 */
		virtual QString tbsId() const;

		static QString makeTbsId(Task* parent, int childPos) {
			return parent->tbsId() + "." + QString::number(childPos);
		}




		QString name() const {
			return m_name;
		}
		void setName(QString name) {
			m_name = name;
			setObjectName(m_name);
		}



		int length() const {
			return m_length;
		}


		int start() const {
			return m_start;
		}

		int finish() const {
			return m_finish;
		}


		int lateStart() const {
			return m_lateStart;
		}

		int lateFinish() const {
			return m_lateFinish;
		}

		int float_() {
			return lateStart() - start();
		}


		bool isCriticalPath() {
			return float_() == 0;
		}



		void registerInLink(Link* l) {
			m_inLinks.append(l);
		}
		void registerOutLink(Link* l) {
			m_outLinks.append(l);
		}

		void unregisterInLink(Link* l) {
			m_inLinks.removeAll(l);
		}
		void unregisterOutLink(Link* l) {
			m_outLinks.removeAll(l);
		}

		int inLinkCount() {
			return m_inLinks.count();
		}

		int outLinkCount() {
			return m_outLinks.count();
		}

		Link::const_iterator inLinkBegin() const {
			return m_inLinks.begin();
		}
		Link::const_iterator inLinkEnd() const {
			return m_inLinks.end();
		}

		Link::iterator inLinkBegin() {
			return m_inLinks.begin();
		}
		Link::iterator inLinkEnd() {
			return m_inLinks.end();
		}


		Link::const_iterator outLinkBegin() const {
			return m_outLinks.begin();
		}
		Link::const_iterator outLinkEnd() const {
			return m_outLinks.end();
		}

		Link::iterator outLinkBegin() {
			return m_outLinks.begin();
		}
		Link::iterator outLinkEnd() {
			return m_outLinks.end();
		}

		bool isBegin() {
			return m_inLinks.isEmpty();
		}

		bool isEnd() {
			return m_outLinks.isEmpty();
		}


		virtual void reset () {
			m_start = 0;
			m_finish = 0;
			m_lateStart = 0;
			m_lateFinish = 0;
		}

		virtual void preSchedule();

		virtual void forwSchedule(Link* inLink) =0;
		virtual void backwSchedule(Link* outLink) =0;

		virtual void postSchedule();


		//	void indent();

		//	void unindent();


		bool canIndent();

		bool canUnindent();




		virtual QString elementName() const {
			return TASK_ELEMENT_NAME;
		}

		virtual void loadFromElement (const QDomElement& element);
		virtual QDomElement saveToElement (QDomDocument& doc) const;


	signals:

		void taskValueChanged(PC::Task* task, PC::Task::Data data);
		void taskTimeChanged(PC::Task* task);

	protected:

		Cycle*	m_cycle;
		MacroTask*	m_parent;

		int m_num;

		QString	m_name;

		int m_length;
		int m_start;
		int m_finish;
		int m_lateStart;
		int m_lateFinish;

		QList<Link*>			m_inLinks;
		QList<Link*>			m_outLinks;

		int m_length_old;
		int m_start_old;
		int m_finish_old;
		int m_lateStart_old;
		int m_lateFinish_old;

		QList<int> m_fTmps;
		QList<int> m_bTmps;

		int minBTmp();
		int maxFTmp();

	};





	class SignalDisabler {
	public:
		SignalDisabler(Task* t)
		: m_t(t) {
			m_origstate = t->blockSignals(true);
		}
		~SignalDisabler() {
			m_t->blockSignals(m_origstate);
		}

	private:
		Task* m_t;
		bool m_origstate;
	};



}


#endif /* __pc_task_h__ */
